package com.coder4j.platform;

import com.alibaba.fastjson.JSON;
import com.coder4j.starter.tools.enums.SeparatorEnums;
import com.coder4j.starter.tools.util.DateUtils;
import com.coder4j.starter.web.DataScope;
import com.coder4j.starter.web.api.ApiNote;
import com.coder4j.platform.entity.AuthApiEntity;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * @author Keller
 * @date 2018-10-09 09:37:33
 */
@Slf4j
public class ApiUtils {

//    public static void main(String[] args) {
//        getApiList("keller",new ArrayList<Class>(){{add(SysDictController.class);}});
//    }

    public static final String prefix = "/api";

    /**
     * 初始化路由
     * 解析传入的Controller对象，解析类名前的@RequestMapping注解
     * 遍历该类的方法：
     * 1.解析有@RouterAttribute注解的方法，
     * 2.将@RouterAttribute和@RequestMapping注解信息取出
     * 3.将方法名、参数类型、参数名取出
     * 4.将解析出的信息组装成一个Api对象
     *
     * @param serviceName 组件名
     * @return
     */

    public static List<AuthApiEntity> getApiList(String serviceName,Set<Class<?>> classes) {
        List<AuthApiEntity> list = new ArrayList<>();
        for (Class<?> clazz : classes) {
            //解析类上的RequestMapping 路由前缀
            String urlPrefix = getUrlPrefix(clazz);
            if (urlPrefix == null) {
                continue;
            }
            //获取Class的方法列表
            Method[] methods = clazz.getMethods();
            Set<String> nameSet = new HashSet<>();
            for (Method method : methods) {

                String methodName = method.getName();

                //过滤@Override修饰的方法
                if(nameSet.contains(methodName)){
                    continue;
                }

                //获取方法上的注解
                ApiNote apiNote = method.getAnnotation(ApiNote.class);
                if (apiNote == null) {
                    continue;
                }


                //解析方法上的 RequestMapping 相关属性
                ReqMapping reqMapping = getReqMapping(method);
                if (reqMapping == null) {
                    continue;
                }
                if (reqMapping.getValue() == null) {
                    reqMapping.setValue("");
                }
                AuthApiEntity api = new AuthApiEntity();
                api.setRequestMethod(reqMapping.getMethod());
                api.setName(apiNote.name());
                api.setCreateName(apiNote.createName());
                api.setCreateTime(DateUtils.parseDateTime(apiNote.createTime()));
                api.setAppName(serviceName);
                api.setControllerName(clazz.getSimpleName());
                api.setMethodName(method.getName());
                api.setUrl(prefix + urlPrefix + reqMapping.getValue());
                api.setParameter(getMethodParametersStr(method));
//                api.setSignatureCode(api.getRequestMethod()  + SeparatorEnums.Colon.value  + api.getAppName() + api.getUrl());

                api.setSignatureCode(api.getRequestMethod()  + SeparatorEnums.Colon.value  + api.getUrl());
                DataScope dataScope = method.getAnnotation(DataScope.class);
                api.setUseDataScope(dataScope == null ? 0 : 1);

                nameSet.add(methodName);

                list.add(api);
            }
        }
        log.info("getApiList  {}", JSON.toJSONString(list));
        return list;
    }
    /**
     * 获取Class上的RequestMapping信息（路由前缀）
     *
     * @param clazz
     * @return
     */
    private static String getUrlPrefix(Class clazz) {
        Annotation[] annotations = clazz.getAnnotations();
        if (annotations == null || annotations.length == 0) {
            return null;
        }
        boolean isController = false;
        String urlPrefix = null;
        for (Annotation annotation : annotations) {
            if (annotation instanceof Controller || annotation instanceof RestController) {
                isController = true;
                continue;
            }
            if (annotation instanceof RequestMapping) {
                RequestMapping requestMapping = (RequestMapping) clazz.getAnnotation(RequestMapping.class);
                if (requestMapping != null) {
                    urlPrefix = String.join(",", requestMapping.value());
                }
            }
        }

        //如果该类是 Controller 类，返回路由前缀
        if (isController) {
            return urlPrefix == null ? "" : urlPrefix;
        }
        //不是 Controller 类，返回空
        return null;

    }

    /**
     * 获取一个方法的参数类型和参数列表 格式: 参数类型:参数名,参数类型:参数名......
     *
     * @param method Method对象
     * @return
     */
    private static String getMethodParametersStr(Method method) {
        StringBuilder builder = new StringBuilder();
        LocalVariableTableParameterNameDiscoverer parameterNameDiscoverer
                = new LocalVariableTableParameterNameDiscoverer();
        //取参数名列表
        String[] params = parameterNameDiscoverer.getParameterNames(method);
        //取参数类型列表
        Class[] paramTypes = method.getParameterTypes();

        for (int i = 0; i < params.length; i++) {
            builder.append(paramTypes[i].getSimpleName())
                    .append(":")
                    .append(params[i]);
            if (i < params.length - 1) {
                builder.append(",");
            }
        }
        return builder.toString();
    }

    private static ReqMapping getReqMapping(Method method) {

        Annotation[] annotations = method.getAnnotations();
        if (annotations == null || annotations.length == 0) {
            return null;
        }

        ReqMapping reqMapping = new ReqMapping();

        for (Annotation annotation : annotations) {
            if (annotation instanceof RequestMapping) {
                reqMapping.setValue(String.join(",", ((RequestMapping) annotation).value()));
                List<String> methods = new ArrayList<>();
                for (RequestMethod requestMethod : ((RequestMapping) annotation).method()) {
                    methods.add(requestMethod.name());
                }
                if (methods.isEmpty()) {
                    reqMapping.setMethod("*");
                } else {
                    reqMapping.setMethod(String.join(",", methods));
                }
                return reqMapping;
            }
            if (annotation instanceof PostMapping) {
                reqMapping.setValue(String.join(",", ((PostMapping) annotation).value()));
                reqMapping.setMethod(RequestMethod.POST.name());
                return reqMapping;
            }
            if (annotation instanceof PutMapping) {
                reqMapping.setValue(String.join(",", ((PutMapping) annotation).value()));
                reqMapping.setMethod(RequestMethod.PUT.name());
                return reqMapping;
            }
            if (annotation instanceof GetMapping) {
                reqMapping.setValue(String.join(",", ((GetMapping) annotation).value()));
                reqMapping.setMethod(RequestMethod.GET.name());
                return reqMapping;
            }
            if (annotation instanceof DeleteMapping) {
                reqMapping.setValue(String.join(",", ((DeleteMapping) annotation).value()));
                reqMapping.setMethod(RequestMethod.DELETE.name());
                return reqMapping;
            }
        }
        return null;
    }

    @Data
    public static class ReqMapping {
        private String value;
        private String method;
    }
}

